﻿using Emgu.CV.CvEnum;
using Emgu.CV;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.IO;
using System.Windows;
using System.Threading;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Diagnostics.Metrics;
using System.Diagnostics;
using Emgu.CV.Structure;
using System.Windows.Documents;

namespace Yang
{
    public class Game
    {
        public ScreenShot _screen;
        private bool _running;
        public static string _basePath = "";
        private Task? _task;
        public event Action<string>? OnLog;
        public event Action<List<Block>>? OnHistoryChange;
        private static Dictionary<int, Mat> _blockImgs = new Dictionary<int, Mat>();
        private static List<Dictionary<int, Mat>> _TImgs = new List<Dictionary<int, Mat>>();
        private List<Block> _history = new List<Block>();
        private List<Block> _current = new List<Block>();
        private List<Block> _clickQueue = new List<Block>();
        private List<Block> _lastFinish = new List<Block>();
        private ImreadModes _colorMode = ImreadModes.Color;
        private bool _writeCVLog = false;
        private int _deepth = 0;
        private bool _mannulMode = false;
        private CNxMouseHook _mouseHook;
        private bool RandomSelect = false;

        public Game()
        {
            _screen = new ScreenShot();
            WindowAPI.OnLog += WindowAPI_OnLog;
            _running = false;
            var path = System.Reflection.Assembly.GetExecutingAssembly().Location.Replace("Yang.dll", "");
            _basePath = path;
            if (_blockImgs.Count == 0)
            {
                List<double> scales = new List<double>() { 1, 0.95, 0.93, 0.91, 0.89, 0.87, 0.85 };
                foreach (var s in scales)
                {
                    Dictionary<int, Mat> tempimgs = new Dictionary<int, Mat>();
                    for (int i = 1; i <= 20; i++)
                    {
                        var p = $"{_basePath}Imgs\\{i}.bmp";
                        if (File.Exists(p))
                        {
                            Image b = Bitmap.FromFile(p);
                            var small = b.GetThumbnailImage((int)(b.Width * s), (int)(b.Height * s), null, IntPtr.Zero);
                            small.Save($"{_basePath}Imgs\\{i}s{s}.bmp");
                            Mat Src2 = CvInvoke.Imread($"{_basePath}Imgs\\{i}s{s}.bmp", _colorMode);
                            tempimgs.Add(i, Src2);
                        }
                    }
                    _TImgs.Add(tempimgs);
                }
            }
            _mouseHook = new CNxMouseHook();
            _mouseHook.MouseEvent += _mouseHook_MouseEvent;
            _mouseHook.Start();
        }

        private void _mouseHook_MouseEvent(object? sender, CNxHookEventArgs e)
        {
            if (_mannulMode == true && _running == true)
            {
                if (e.MouseButton == System.Windows.Input.MouseButton.Left && e.ButtonActionType == ENxButtonActionTypes.MouseUp)
                {
                    OnLog?.Invoke($"MouseClick x:{(int)e.Location.X - _screen.windowRect.left}, y:{(int)e.Location.Y - _screen.windowRect.top}");
                    if (_clickQueue.Count == 0)
                    {
                        var the = _history.Find(x => x.Visiable == true && x.VisiableLevel == 0 && x.Area.Contains((int)e.Location.X - _screen.windowRect.left, (int)e.Location.Y - _screen.windowRect.top) == true);
                        if (the != null)
                        {
                            OnLog?.Invoke($"Found {the.Info}");
                            _clickQueue.Add(the);
                            Thread.Sleep(1500);
                            _screen.GetImage(_basePath + "Imgs\\base.bmp");
                            FindPic();
                            OnHistoryChange?.Invoke(_history);
                        }
                    }
                }
            }
        }

        private void WindowAPI_OnLog(string obj)
        {
            OnLog?.Invoke(obj);
        }

        public void Test()
        {
            if (_screen == null)
            {
                _screen = new ScreenShot();
            }
            _screen.GetImage(_basePath + "Imgs\\base.bmp");
            Mat sup;
            Rectangle recup;
            Bitmap tb = new Bitmap(_basePath + "Imgs\\base.bmp");
            if (tb == null)
            {
                return;
            }
            recup = new Rectangle(0, 0, _screen.windowRect.right - _screen.windowRect.left, (int)((_screen.windowRect.bottom - _screen.windowRect.top) * 0.75));

            var bg1 = tb.Clone(recup, tb.PixelFormat);
            bg1.Save(_basePath + "Imgs\\base1.bmp");
            bg1.Dispose();
            sup = CvInvoke.Imread(_basePath + "Imgs\\base1.bmp", _colorMode);
            File.Delete(_basePath + "Imgs\\base1.bmp");
            tb.Dispose();
            File.Delete(_basePath + "Imgs\\base.bmp");

            List<(int btype, Rectangle area)> temp1 = new List<(int, Rectangle)>();
            List<(int btype, Rectangle area)> temp2 = new List<(int, Rectangle)>();
            int lastcount = 0;
            foreach (var img in _TImgs)
            {
                int thiscount = 0;
                foreach (var item in img)
                {
                    var test = GetMatchPos(item.Value, sup, recup.X, recup.Y, 0);
                    thiscount += test.Count;
                }
                if (thiscount > lastcount)
                {
                    lastcount = thiscount;
                    _blockImgs = img;
                }
            }
        }

        public async Task StartAsync(bool? randomselect, int? maxstep)
        {
            this.RandomSelect = randomselect ?? false;
            this._deepth = maxstep ?? 6;
            //_screen = new ScreenShot("StartupDui", "多屏协同");
            OnLog?.Invoke($"Started");
            await Task.Delay(100);
            _history.ForEach(x =>
            {
                x.Visiable = true;
                _ = x.UpperBlocks;
                _ = x.AllLowers;
            });

            OnHistoryChange?.Invoke(_history);
            _clickQueue.Clear();
            _current.Clear();
            if (_blockImgs.Count == 0)
            {
                Test();
            }
            _running = true;


            _mannulMode = false;
            //_screen.GetImage(_basePath + "Imgs\\base.bmp");
            //FindPic();
            //OnHistoryChange?.Invoke(_history);


            ChangeLevel();
            _task = Task.Run(async () =>
            {
                if (_mannulMode == false)
                {
                    CaculatePathAsync();
                    if (_clickQueue.Count > 0)
                    {
                        var x = _clickQueue[0].Area.X + _clickQueue[0].Area.Width / 2 + _screen.windowRect.left;
                        var y = _clickQueue[0].Area.Y + _clickQueue[0].Area.Height / 2 + _screen.windowRect.top;
                        WindowAPI.MouseLeftClickEvent(x, y, 0);
                    }
                    await Task.Delay(1500);
                }
                while (_running && _mannulMode == false)
                {
                    _screen.GetImage(_basePath + "Imgs\\base.bmp");
                    FindPic();
                    OnHistoryChange?.Invoke(_history);
                    CaculatePathAsync();
                    if (_clickQueue.Count > 0)
                    {
                        var x = _clickQueue[0].Area.X + _clickQueue[0].Area.Width / 2 + _screen.windowRect.left;
                        var y = _clickQueue[0].Area.Y + _clickQueue[0].Area.Height / 2 + _screen.windowRect.top;
                        WindowAPI.MouseLeftClickEvent(x, y, 0);
                        //OnLog?.Invoke($"SetMouse x:{x}, y:{y}");
                    }
                    await Task.Delay(1500);
                }
                OnLog?.Invoke($"OutLoop");
                return Task.CompletedTask;
            });
        }

        private void CaculatePathAsync()
        {
            if (_clickQueue.Count == 0 && _history.Count > 0)
            {
                List<int> tempid = new List<int>();
                OnLog?.Invoke($"正在根据已有数据计算最佳路径，深度{_deepth}请等待。。。");
                DateTime d1 = DateTime.Now;
                var rv = Cal(_history, _current, tempid);
                OnLog?.Invoke($"计算完成，耗时{DateTime.Now.Subtract(d1).TotalSeconds}");

                foreach (var item in rv.ids)
                {
                    var a = _history.Find(x => x.ID == item);
                    if (a != null)
                    {
                        _clickQueue.Add(a);
                    }
                }

                OnLog?.Invoke($"计算路径:{_clickQueue.Count}:");
                foreach (var item in _clickQueue)
                {
                    OnLog?.Invoke($"{item.Info}");
                }
                if (_clickQueue.Count == 0 && _current.Count < 6)
                {
                    if (RandomSelect == true)
                    {
                        var random = _history.Where(x => x.Visiable == true && x.VisiableLevel == 0).OrderBy(x => x.Level).FirstOrDefault();
                        if (random != null)
                        {
                            _clickQueue.Add(random);
                            OnLog?.Invoke($"{random.Info}");
                        }
                    }
                    else
                    {
                        _mannulMode = true;
                        OnLog?.Invoke($"手动模式开启");
                    }
                }

            }
        }

        private void CaculatePathAsync2()
        {
            if (_clickQueue.Count == 0 && _history.Count > 0)
            {
                List<int> tempid = new List<int>();
                OnLog?.Invoke($"正在反向己算");
                DateTime d1 = DateTime.Now;
                var rv = Cal2();
                OnLog?.Invoke($"计算完成，耗时{DateTime.Now.Subtract(d1).TotalSeconds}");

                foreach (var item in rv)
                {
                    var a = _history.Find(x => x.ID == item);
                    if (a != null)
                    {
                        _clickQueue.Add(a);
                    }
                }

                OnLog?.Invoke($"计算路径:{_clickQueue.Count}:");
                foreach (var item in _clickQueue)
                {
                    OnLog?.Invoke($"{item.Info}");
                }
                OnLog?.Invoke($"共:{_clickQueue.Count}步");
                if (_clickQueue.Count == 0 && _current.Count < 6)
                {
                    if (RandomSelect == true)
                    {
                        var random = _history.Where(x => x.Visiable == true && x.VisiableLevel == 0).OrderBy(x => x.Level).FirstOrDefault();
                        if (random != null)
                        {
                            _clickQueue.Add(random);
                        }
                    }
                    else
                    {
                        _mannulMode = true;
                        OnLog?.Invoke($"手动模式开启");
                    }
                }

            }

        }

        private void CaculatePathAsync3()
        {
            if (_clickQueue.Count == 0 && _history.Count > 0)
            {
                OnLog?.Invoke($"正在根据已有数据计算最佳路径...");
                DateTime d1 = DateTime.Now;
                var rv = Cal3(_history, _current);
                OnLog?.Invoke($"计算完成，耗时{DateTime.Now.Subtract(d1).TotalSeconds}");

                foreach (var item in rv)
                {
                    var a = _history.Find(x => x.ID == item);
                    if (a != null)
                    {
                        _clickQueue.Add(a);
                    }
                }

                OnLog?.Invoke($"计算路径:");
                foreach (var item in _clickQueue)
                {
                    OnLog?.Invoke($"{item.Info}");
                }
                if (_clickQueue.Count == 0 && _current.Count < 6)
                {
                    if (RandomSelect == true)
                    {
                        var random = _history.Where(x => x.Visiable == true && x.VisiableLevel == 0).OrderBy(x => x.Level).FirstOrDefault();
                        if (random != null)
                        {
                            _clickQueue.Add(random);
                        }
                    }
                    else
                    {
                        _mannulMode = true;
                        OnLog?.Invoke($"手动模式开启");
                    }
                }
                OnLog?.Invoke($"共{_clickQueue.Count}步");
            }
        }

        private (List<int> ids, int validcount) Cal(List<Block> needcheck, List<Block> curr, List<int> checkedids, int level = 1)
        {
            DateTime start = DateTime.Now;
            List<(List<int> ids, int validcount)> rv = new List<(List<int>, int)>();
            List<BlockGroup> bgs = new List<BlockGroup>();
            bgs = GetMoves(needcheck, curr);

            if (bgs.Count == 0)
            {
                rv.Add((checkedids, checkedids.Count - curr.Count * 100));
            }
            bgs = bgs.OrderByDescending(x => x.ValidClick).Take(3).ToList();

            var final = new List<BlockGroup>();
            foreach (var b in bgs)
            {
                var tempids = checkedids.ToList();
                var his = needcheck.Clone();
                var ids = b.Blocks.Select(x => x.ID);
                tempids.AddRange(ids);
                his.Where(x => ids.Contains(x.ID)).ToList().ForEach(x => x.Visiable = false);
                if (level >= _deepth)
                {
                    rv.Add((tempids, tempids.Count - b.Current.Count * 100));
                }
                else
                {
                    rv.Add(Cal(his, b.Current, tempids, level + 1));
                }
            }

            var time = DateTime.Now.Subtract(start).TotalSeconds;
            if (time > 60 || level <= 3)
            {
                Trace.WriteLine($"Level:{level},data:{bgs.Count},  耗时：{time}");
            }
            return rv.OrderByDescending(x => x.validcount).FirstOrDefault();
        }

        private void ChangeLevel()
        {
            List<List<Block>> regions = new List<List<Block>>();

            foreach (var h in _history)
            {
                var found = false;
                List<Block> ids = new List<Block>();
                ids.Add(h);
                ids.AddRange(h.GetAllLowers());
                foreach (var r in regions)
                {
                    foreach (var id in ids)
                    {
                        if (r.Any(x => x.ID == id.ID))
                        {
                            found = true;
                            foreach (var newid in ids)
                            {
                                if (r.Any(x => x.ID == newid.ID) == false)
                                {
                                    r.Add(newid);
                                }
                            }
                            break;
                        }
                    }
                    if (found == true)
                    {
                        break;
                    }
                }
                if (found == false)
                {
                    List<Block> b = new List<Block>();
                    b.AddRange(h.GetAllLowers());
                    b.Add(h);
                    regions.Add(b);
                }
            }

            var count = 0;
            regions = regions.OrderByDescending(x => x.Count).ToList();
            foreach (var item in regions)
            {
                count++;
                item.ForEach(x => x.Region = count);
            }

            //int maxlevel = regions.Max(x => x.Max(y => y.Level));
            //foreach (var r in regions)
            //{
            //    int ml = r.Max(x => x.Level);
            //    if (ml < maxlevel)
            //    {
            //        foreach (var b in r)
            //        {
            //            b.Level += maxlevel - ml;
            //        }
            //    }
            //}

        }
        private List<int> Cal2()
        {
            List<Block> rv = new List<Block>();
            List<Block> his = new List<Block>();
            foreach (var h in _history)
            {
                his.Add(new Block(new List<Block>())
                {
                    Area = h.Area,
                    Visiable = h.Visiable,
                    BlockType = h.BlockType,
                    ID = h.ID,
                    Level = h.Level
                });
            }
            foreach (var h in _history)
            {
                var upids = h.Upper.Select(x => x.ID);
                var f = his.Find(x => x.ID == h.ID);
                if (f != null)
                {
                    f.Upper = his.Where(x => upids.Contains(x.ID)).ToList();
                }
            }
            foreach (var h in _history)
            {
                var lowerids = h.Lower.Select(x => x.ID);
                var f = his.Find(x => x.ID == h.ID);
                if (f != null)
                {
                    f.Lower = his.Where(x => lowerids.Contains(x.ID)).ToList();
                }
            }
            var maxLevel = his.Where(x => x.Visiable == true).Max(x => x.Level);
            var number = his.Where(x => x.Visiable == true).Union(_current).GroupBy(x => x.BlockType).Select(x => new { t = x.Key, c = x.Count() }).ToList();
            Dictionary<int, int> skip = new Dictionary<int, int>();
            foreach (var item in number)
            {
                var remain = item.c % 3;
                skip.Add(item.t, remain);
            }

            for (int i = maxLevel; i >= 0; i--)
            {
                var bs = his.Where(x => x.Level == i && x.Visiable == true).OrderBy(x => x.BlockType).ToList();

                foreach (var b in bs)
                {
                    if (skip[b.BlockType] > 0 && b.Lower.Count == 0)
                    {
                        skip[b.BlockType]--;
                        b.Visiable = false;
                    }
                    else if (b.Visiable == true)
                    {
                        rv.Add(b);
                        b.Visiable = false;
                        var samelevel = bs.Where(x => x.Visiable == true && x.BlockType == b.BlockType).Take(2).ToList();
                        rv.AddRange(samelevel);
                        samelevel.ForEach(x => x.Visiable = false);
                        if (samelevel.Count < 2)
                        {
                            int needmore = 2 - samelevel.Count;
                            var uppers = his.Where(x => x.Level < i && x.Visiable == true && x.BlockType == b.BlockType).OrderByDescending(x => x.Level).Take(needmore).ToList();
                            rv.AddRange(uppers);
                            uppers.ForEach(x => x.Visiable = false);
                        }
                    }
                }
            }
            rv.Reverse();

            for (int i = 0; i < rv.Count; i++)
            {
                var ups = _history.Find(x => x.ID == rv[i].ID)?.GetUpperBlocks().ToList();
                int pos = i;
                if (ups.Count > 0)
                {
                    for (int j = 0; j < ups.Count; j++)
                    {
                        var pid = rv.FindIndex(x => x.ID == ups[j].ID);
                        if (pid > pos)
                        {
                            rv.RemoveAt(pid);
                            rv.Insert(pos, ups[j]);
                            i++;
                        }
                        if (pid == -1 && ups[j].Visiable == true)
                        {
                            rv.Insert(pos, ups[j]);
                            i++;
                        }
                    }
                }
            }


            return rv.Select(x => x.ID).ToList();
        }

        private List<int> Cal3(List<Block> history, List<Block> curr, int level = 0)
        {
            Trace.WriteLine($"level:{level}");
            List<int> rv = new List<int>();
            List<BlockGroup> bgs = GetMoves(history, curr, level);

            if (level > 24)
            {
                return new List<int>();
            }
            if (bgs.Count == 0)
            {
                //bgs = GetMoves(history, curr, level,2);
                //if (bgs.Count == 0 || bgs[0].Blocks.Count+curr.Count>=7)
                //{
                level++;
                //}
                //else
                //{
                //    int a = 0;
                //}
            }

            List<Block> his = history.Clone();
            var cur = curr.ToList();
            if (bgs.Count > 0)
            {
                bgs = bgs.OrderBy(x => x.Blocks.Sum(y => y.VisiableSteps)).OrderByDescending(x => x.Blocks.Sum(y => y.AllLowers)).ToList();
                var thebg = bgs[0];
                var last = cur.ToList();
                List<Block> toadd = new List<Block>();
                foreach (var item in thebg.Blocks)
                {
                    var theitem = his.Find(x => x.ID == item.ID);
                    if (theitem != null)
                    {
                        toadd.Add(theitem);
                        last.Add(theitem);
                    }
                    var exists = last.Where(x => x.BlockType == item.BlockType).ToList();
                    if (exists.Count == 3)
                    {
                        exists.ForEach(x => last.Remove(x));
                    }
                }

                if (last.Count < 7)
                {
                    toadd.ForEach(x => x.Visiable = false);
                    cur = last.ToList();
                    rv.AddRange(thebg.Blocks.Select(x => x.ID));
                    rv.Add(-1);
                }
                else
                {
                    return new List<int>();
                }
            }
            rv.AddRange(Cal3(his, cur, level));
            return rv;
        }

        private List<BlockGroup> GetMoves(List<Block> history, List<Block> curr, int? maxlevel = null, int take = 3)
        {
            DateTime start = DateTime.Now;
            List<BlockGroup> bgs = new List<BlockGroup>();
            var list = history.Where(x => x.Visiable == true).GroupBy(x => x.BlockType, x => x.Level).Select(x => new { key = x.Key, count = x.Count(), v = x.Max() }).ToList();
            var select = list.OrderBy(x => x.v).ToList();

            foreach (var s in select)
            {
                var exist = curr.Where(x => x.BlockType == s.key).Count();
                var subs = history.Where(x => x.BlockType == s.key && x.Visiable == true).OrderBy(x => x.VisiableSteps).Select(x => x.ID).Take(6).ToArray();
                List<int[]> all = new List<int[]>();
                if (subs.Length <= take - exist)
                {
                    all.Add(subs);
                }
                else
                {
                    all = PermutationAndCombination<int>.GetCombination(subs, take - exist);
                }
                if (all == null)
                {
                    continue;
                }
                foreach (var a in all)
                {
                    var curr2 = curr.ToList();
                    List<Block> data = new List<Block>();
                    if (a.Length == 0 || a.Length < take - exist)
                    {
                        continue;
                    }
                    var testb = history.Where(x => a.Contains(x.ID)).ToList();
                    int over = 0;
                    foreach (var item in testb)
                    {
                        var parents = item.UpperVisiableBlocks.OrderBy(x => x.Level).ToList();
                        if (parents.Count > 0)
                        {
                            foreach (var pp in parents)
                            {
                                if (data.Any(x => x.ID == pp.ID) == false)
                                {
                                    if (pp.BlockType == s.key)
                                    {
                                        over++;
                                    }
                                    data.Add(pp);
                                }
                            }
                        }
                        if (data.Any(x => x.ID == item.ID) == false)
                        {
                            data.Add(item);
                        }
                    }
                    if (data.Where(x => x.BlockType == testb[0].BlockType).Count() > testb.Count)
                    {
                        continue;
                    }
                    bool end = false;
                    foreach (var d in data)
                    {
                        curr2.Add(d);
                        var exists = curr2.Where(x => x.BlockType == d.BlockType).ToList();
                        if (exists.Count == 3)
                        {
                            exists.ForEach(x => curr2.Remove(x));
                        }
                        if (curr2.Count >= 7)
                        {
                            end = true;
                            break;
                        }
                    }
                    if (end == false)
                    {
                        bgs.Add(new BlockGroup(data) { ValidClick = data.Count - curr2.Count * 100 + data.Sum(x => x.AllLowers), Current = curr2 });
                    }
                }
            }
            //int min = bgs.Min(x => x.Blocks.Count);
            Trace.WriteLine($"GetMove: {DateTime.Now.Subtract(start).TotalSeconds}");
            return bgs;// bgs.Where(x=>x.Blocks.Count == min).ToList();
        }


        private void FindPic()
        {
            Mat sup;
            Rectangle recup;
            Bitmap tb = new Bitmap(_basePath + "Imgs\\base.bmp");
            if (tb == null)
            {
                return;
            }
            if (_clickQueue.Count > 0)
            {
                recup = Rectangle.Inflate(_clickQueue[0].Area, _clickQueue[0].Area.Width, _clickQueue[0].Area.Height);
                if (recup.X <= 0)
                {
                    recup.X = 0;
                }
                if (recup.Y <= 0)
                {
                    recup.Y = 0;
                }
                if (recup.X + recup.Width > _screen.windowRect.right - _screen.windowRect.left)
                {
                    recup.Width = _screen.windowRect.right - _screen.windowRect.left - recup.X;
                }
            }
            else
            {
                recup = new Rectangle(0, 0, _screen.windowRect.right - _screen.windowRect.left, (int)((_screen.windowRect.bottom - _screen.windowRect.top) * 0.75));
            }

            var bg1 = tb.Clone(recup, tb.PixelFormat);
            bg1.Save(_basePath + "Imgs\\base1.bmp");
            bg1.Dispose();
            sup = CvInvoke.Imread(_basePath + "Imgs\\base1.bmp", _colorMode);
            File.Delete(_basePath + "Imgs\\base1.bmp");

            //recdown = new Rectangle(40, 1390, 600, 110);
            //var bg2 = tb.Clone(recdown, tb.PixelFormat);
            //bg2.Save(_basePath + "Imgs\\base2.bmp");
            //bg2.Dispose();
            //sdown = CvInvoke.Imread(_basePath + "Imgs\\base2.bmp", _colorMode);
            //File.Delete(_basePath + "Imgs\\base2.bmp");
            tb.Dispose();
            File.Delete(_basePath + "Imgs\\base.bmp");


            List<(int btype, Rectangle area)> temp = new List<(int, Rectangle)>();
            foreach (var item in _blockImgs)
            {
                if (_writeCVLog == true)
                {
                    OnLog?.Invoke($"Find:{item.Key}");
                }
                var test = GetMatchPos(item.Value, sup, recup.X, recup.Y, 0);
                foreach (var find in test)
                {
                    temp.Add((item.Key, find));
                }

                //var test2 = GetMatchPos(_UnderImgs.Where(x => x.Key == item.Key).Select(x => x.Value).FirstOrDefault() ?? item.Value, sdown, recdown.X, recdown.Y, 0);
                //foreach (var find in test2)
                //{
                //    temp.Add((item.Key, find));
                //}
            }
            if (_clickQueue.Count > 0)
            {
                _current.Add(new Block(new List<Block>())
                {
                    ID = _clickQueue[0].ID,
                    BlockType = _clickQueue[0].BlockType,
                    Level = -1,
                    Visiable = true
                });
                var dels = _current.Where(x => x.BlockType == _current.Last().BlockType).Take(3).ToList();
                if (dels.Count == 3)
                {
                    dels.ForEach(x => _current.Remove(x));
                }
                var the = _clickQueue[0];
                _clickQueue[0].Visiable = false;
                if (the.Lower.Count > 0)
                {
                    for (int i = 0; i < temp.Count; i++)
                    {
                        if (_clickQueue[0].Lower.Any(x => x.Area.IntersectsWith(temp[i].area)))
                        {
                            temp.RemoveAt(i);
                            i--;
                        }
                    }
                }
                _clickQueue.RemoveAt(0);
            }
            //OnLog?.Invoke($"Current:{_current.Count}");

            foreach (var item in temp)
            {
                Block b = new Block(_history)
                {
                    BlockType = item.btype,
                    //Level = maxLevel,
                    Area = item.area
                };

                var tops = _history.Where(x => x.Visiable == false && x.Area.IntersectsWith(b.Area)).OrderByDescending(x => x.Level).ToList();
                var del = new List<Block>();
                foreach (var t in tops)
                {
                    var up = t.GetUpperBlocks().Select(x => x.ID);
                    del.AddRange(tops.Where(x => up.Contains(x.ID)));
                }
                del.ForEach(x => tops.Remove(x));
                if (tops.Count == 0)
                {
                    b.Level = 0;
                }
                else
                {
                    b.Level = tops.Max(x => x.Level) + 1;
                    b.Upper.AddRange(tops);
                }
                if (_history.Any(x => x.Level == b.Level && x.Area.X == b.Area.X && x.Area.Y == b.Area.Y) == false)
                {
                    foreach (var top in tops)
                    {
                        top.Lower.Add(b);
                    }
                    _history.Add(b);
                    OnLog?.Invoke($"=====History Add:{b.Info}=====");
                }
            }
        }
        private List<Rectangle> GetMatchPos(Mat Template, Mat source, int basex, int basey, double accoffset)

        {
            List<Rectangle> rv = new List<Rectangle>();

            Mat MatchResult = new();//匹配结果
            CvInvoke.MatchTemplate(source, Template, MatchResult, Emgu.CV.CvEnum.TemplateMatchingType.CcorrNormed);//使用相关系数法匹配
            var m = new Matrix<float>(MatchResult.Rows, MatchResult.Cols);
            if (_writeCVLog == true)
            {
                OnLog?.Invoke($"Max:{MatchResult.GetValueRange().Max}, Min:{MatchResult.GetValueRange().Min}");
            }
            MatchResult.CopyTo(m);
            for (int i = 0; i < MatchResult.Rows; i++)
            {
                for (int j = 0; j < MatchResult.Cols; j++)
                {
                    if (m[i, j] > (0.99 - accoffset))
                    {
                        if (rv.Any(x => Math.Abs(x.X - j - basex) < Template.Width && Math.Abs(x.Y - i - basey) < Template.Height) == false)
                        {
                            rv.Add(new Rectangle(j + basex, i + basey, Template.Width, Template.Height));
                        }
                    }
                }
            }
            return rv;
        }

        public async Task TestPathAsync(int deepth)
        {
            ChangeLevel();
            OnLog?.Invoke($"正在根据已有数据计算最佳路径，请等待。。。");
            await Task.Delay(100);
            DateTime d1 = DateTime.Now;

            List<int> tempid = new List<int>();
            _deepth = deepth;
            // var topids = _history.OrderByDescending(x => x.Level).ThenByDescending(x => x.ID).First().GetUpperVisiableBlocks().Select(x => x.ID).ToList();
            while (true)
            {
                var rv = Cal(_history, _current, tempid);
                foreach (var id in rv.ids)
                {
                    var item = _history.Find(x => x.ID == id) ?? new Block(new List<Block>());
                    item.Visiable = false;
                    _current.Add(new Block(new List<Block>())
                    {
                        ID = item.ID,
                        BlockType = item.BlockType,
                        Level = -1,
                        Visiable = true
                    });
                    var dels = _current.Where(x => x.BlockType == _current.Last().BlockType).Take(3).ToList();
                    if (dels.Count == 3)
                    {
                        dels.ForEach(x => _current.Remove(x));
                    }

                    OnLog?.Invoke($"{item.Info}");
                }
                OnLog?.Invoke($"测试完成，最大{rv.ids.Count}步，耗时{DateTime.Now.Subtract(d1).TotalSeconds}");
                await Task.Delay(100);
                if (rv.ids.Count == 0)
                {
                    Stop();
                    break;
                }
            }
            //var rv = Cal2();
            //foreach (var id in rv)
            //{
            //    var item = _history.Find(x => x.ID == id) ?? new Block(new List<Block>());
            //    item.Visiable = false;
            //    _current.Add(new Block(new List<Block>())
            //    {
            //        ID = item.ID,
            //        BlockType = item.BlockType,
            //        Level = -1,
            //        Visiable = true
            //    });
            //    var dels = _current.Where(x => x.BlockType == _current.Last().BlockType).Take(3).ToList();
            //    if (dels.Count == 3)
            //    {
            //        dels.ForEach(x => _current.Remove(x));
            //    }

            //    OnLog?.Invoke($"{item.Info}");
            //}
            //OnLog?.Invoke($"测试完成，最大{rv.Count}步，耗时{DateTime.Now.Subtract(d1).TotalSeconds}");

            //var rv = Cal3(_history, _current, 0);
            //var validcount = 0;
            //foreach (var id in rv)
            //{
            //    var item = _history.Find(x => x.ID == id);
            //    if (item == null)
            //    {
            //        OnLog?.Invoke($"==============================");
            //    }
            //    else
            //    {
            //        validcount++;
            //        item.Visiable = false;
            //        OnLog?.Invoke($"{item.Info}");
            //    }
            //}
            //OnLog?.Invoke($"测试完成，最大{validcount}步，耗时{DateTime.Now.Subtract(d1).TotalSeconds}");

            OnHistoryChange?.Invoke(_history);
        }
        public async void Stop()
        {
            //ChangeLevel();
            _running = false;
            if (_task != null)
            {
                await _task;
            }
            var result = _history.GroupBy(x => x.BlockName).Select(x => new { n = x.Key, v = x.Count() }).OrderBy(x => x.v).ToList();
            OnLog?.Invoke($"Stopped");
            OnLog?.Invoke($"当前记录：{_history.Count}，一共消除：{_history.Where(x => x.Visiable == false).Count()}");
            foreach (var item in result)
            {
                OnLog?.Invoke($"{item.n}:{item.v}");
            }
            OnLog?.Invoke($"层分布：");
            var group = _history.GroupBy(x => new { x.BlockType }).OrderBy(x => x.Key.BlockType).ToList();
            foreach (var item in group)
            {
                var list = item.ToList();
                OnLog?.Invoke($"{list[0].BlockName}: level= {string.Join(',', list.OrderBy(x => x.Level).Select(x => x.Level))}");
            }

        }

        public void ClearHistory()
        {
            _history.Clear();
            OnHistoryChange?.Invoke(_history);
        }

        public void Save()
        {
            var text = System.Text.Json.JsonSerializer.Serialize(_history, new System.Text.Json.JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve, MaxDepth = 25600 });
            File.WriteAllText(_basePath + "data.txt", text);
        }

        public void Load()
        {
            var text = File.ReadAllText(_basePath + "data.txt");
            _history = System.Text.Json.JsonSerializer.Deserialize<List<Block>>(text, new System.Text.Json.JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve, MaxDepth = 25600 }) ?? new List<Block>();
            _history.ForEach(x =>
            {
                x.Visiable = true;
                _ = x.UpperBlocks;
                _ = x.AllLowers;
            });
            _clickQueue.Clear();
            _current.Clear();
            OnHistoryChange?.Invoke(_history);
        }
    }
}
